<template>
  <el-dialog v-model="visible" title="编辑数据" width="72%" top="5vh">
    <div>
      <el-scrollbar :height="maxHeight * 0.95 - 200 + 'px'">
        <el-tabs v-model="activeName" class="demo-tabs">
          <el-tab-pane label="节点" name="node">
            <el-table :data="_nodes" style="width: 100%">
              <el-table-column prop="name" label="节点名称">
                <template #default="scope">
                  <el-input
                    v-if="editNodeRowIndex === scope.$index"
                    v-model="scope.row.name"
                    size="small"
                  ></el-input>
                  <span v-else>{{ scope.row.name }}</span>
                </template>
              </el-table-column>
              <el-table-column prop="nodeLabelFontSize" label="节点字体大小">
                <template #default="scope">
                  <el-input
                    v-if="editNodeRowIndex === scope.$index"
                    :model-value="scope.row.nodeLabelFontSize"
                    @input="(val:string) => focusInputNumber('nodeLabelFontSize', scope.$index, val)"
                    size="small"
                  ></el-input>
                  <span v-else>{{
                    scope.row.nodeLabelFontSize &&
                    `${scope.row.nodeLabelFontSize}px`
                  }}</span>
                </template>
              </el-table-column>
              <el-table-column prop="nodeStrokeWidth" label="节点边框宽度">
                <template #default="scope">
                  <el-input
                    v-if="editNodeRowIndex === scope.$index"
                    :model-value="scope.row.nodeStrokeWidth"
                    @input="(val:string) => focusInputNumber('nodeStrokeWidth', scope.$index, val)"
                    size="small"
                  ></el-input>
                  <span v-else>{{
                    scope.row.nodeStrokeWidth &&
                    `${scope.row.nodeStrokeWidth}px`
                  }}</span>
                </template>
              </el-table-column>
              <el-table-column prop="nodeStroke" label="节点边框颜色">
                <template #default="scope">
                  <el-color-picker
                    v-if="editNodeRowIndex === scope.$index"
                    v-model="scope.row.nodeStroke"
                  />
                  <span v-else>{{ scope.row.nodeStroke }}</span>
                </template>
              </el-table-column>
              <el-table-column
                prop="nodeBackgroundType"
                label="节点背景"
                width="200"
              >
                <template #default="scope">
                  <el-radio-group
                    v-if="editNodeRowIndex === scope.$index"
                    v-model="scope.row.nodeBackgroundType"
                  >
                    <el-radio :label="'1'">纯色</el-radio>
                    <el-radio :label="'2'">图片</el-radio>
                  </el-radio-group>
                  <span v-else>{{
                    ["", "纯色", "图片"][scope.row.nodeBackgroundType || 0]
                  }}</span>
                </template>
              </el-table-column>
              <el-table-column
                prop="nodeBackground"
                label="节点背景"
                width="220"
              >
                <template #default="scope">
                  <el-color-picker
                    v-if="
                      editNodeRowIndex === scope.$index &&
                      scope.row.nodeBackgroundType === '1'
                    "
                    v-model="scope.row.nodeBackground"
                  />
                  <el-input
                    v-else-if="
                      editNodeRowIndex === scope.$index &&
                      scope.row.nodeBackgroundType === '2'
                    "
                    v-model="scope.row.nodeBackground"
                    placeholder="请输入图片地址"
                  >
                    <template #append>
                      <div class="table-node-background-preview">
                        <img
                          class="table-node-background-preview__img"
                          :src="scope.row.nodeBackground"
                          alt=""
                          srcset=""
                        />
                      </div>
                    </template>
                  </el-input>
                  <div
                    class="table-node-background-preview"
                    v-else-if="scope.row.nodeBackgroundType === '2'"
                  >
                    <img
                      class="table-node-background-preview__img"
                      :src="scope.row.nodeBackground"
                      alt=""
                      srcset=""
                    />
                  </div>
                  <span v-else>{{ scope.row.nodeBackground }}</span>
                </template>
              </el-table-column>
              <el-table-column fixed="right" label="操作" width="120">
                <template #default="scope">
                  <el-button
                    v-if="editNodeRowIndex === scope.$index"
                    link
                    type="success"
                    size="small"
                    :disabled="scope.row.name === ''"
                    @click="handleEditNodeRow(scope.row, scope.$index)"
                    >完成</el-button
                  >
                  <el-button
                    v-else
                    link
                    type="primary"
                    size="small"
                    :disabled="
                      editNodeRowIndex !== -1 &&
                      editNodeRowIndex !== scope.$index
                    "
                    @click="editNodeRowIndex = scope.$index"
                    >编辑</el-button
                  >
                  <el-button
                    link
                    type="danger"
                    size="small"
                    @click="handleDeleteNodeRow(scope.row)"
                    >删除</el-button
                  >
                </template>
              </el-table-column>
            </el-table>
            <el-button
              style="width: 100%"
              type="primary"
              @click="handleAppendNode"
              :disabled="editNodeRowIndex !== -1"
              >添加节点</el-button
            >
          </el-tab-pane>
          <el-tab-pane label="关系" name="link">
            <el-table :data="_links" style="width: 100%">
              <el-table-column label="起点">
                <template #default="scope">
                  <el-select
                    v-if="scope.row.id === newLinkId"
                    placeholder="请选择"
                    v-model="scope.row.source"
                  >
                    <el-option
                      v-for="item in _nodes"
                      :key="item.id"
                      :value="item.id"
                      :label="item.name"
                    ></el-option>
                  </el-select>
                  <span v-else>{{
                    _nodes.find(({ id }) => id === scope.row.source)?.name
                  }}</span>
                </template>
              </el-table-column>
              <el-table-column label="终点">
                <template #default="scope">
                  <el-select
                    v-if="scope.row.id === newLinkId"
                    placeholder="请选择"
                    v-model="scope.row.target"
                  >
                    <el-option
                      v-for="item in _nodes"
                      :key="item.id"
                      :value="item.id"
                      :label="item.name"
                    ></el-option>
                  </el-select>
                  <span v-else>{{
                    _nodes.find(({ id }) => id === scope.row.target)?.name
                  }}</span>
                </template>
              </el-table-column>
              <el-table-column prop="label" label="关系名称">
                <template #default="scope">
                  <el-input
                    v-if="editLinkRowIndex === scope.$index"
                    v-model="scope.row.label"
                    size="small"
                  ></el-input>
                  <span v-else>{{ scope.row.label }}</span>
                </template>
              </el-table-column>
              <el-table-column prop="linkLabelFontSize" label="连线字体大小">
                <template #default="scope">
                  <el-input
                    v-if="editLinkRowIndex === scope.$index"
                    :model-value="scope.row.linkLabelFontSize"
                    @input="(val:string) => focusInputNumber('linkLabelFontSize', scope.$index, val, '_links')"
                    size="small"
                  ></el-input>
                  <span v-else>{{
                    scope.row.linkLabelFontSize &&
                    `${scope.row.linkLabelFontSize}px`
                  }}</span>
                </template>
              </el-table-column>
              <el-table-column prop="linkStroke" label="连线颜色">
                <template #default="scope">
                  <el-color-picker
                    v-if="editLinkRowIndex === scope.$index"
                    v-model="scope.row.linkStroke"
                  />
                  <span v-else>{{ scope.row.linkStroke }}</span>
                </template>
              </el-table-column>
              <el-table-column prop="linkStroke" label="连线文字颜色">
                <template #default="scope">
                  <el-color-picker
                    v-if="editLinkRowIndex === scope.$index"
                    v-model="scope.row.linkLabelColor"
                  />
                  <span v-else>{{ scope.row.linkLabelColor }}</span>
                </template>
              </el-table-column>
              <el-table-column fixed="right" label="操作" width="120">
                <template #default="scope">
                  <el-button
                    v-if="editLinkRowIndex === scope.$index"
                    link
                    type="success"
                    size="small"
                    :disabled="scope.row.label === ''"
                    @click="handleEditLinkRow(scope.row, scope.$index)"
                    >完成</el-button
                  >
                  <el-button
                    v-else
                    link
                    type="primary"
                    size="small"
                    :disabled="
                      editLinkRowIndex !== -1 &&
                      editLinkRowIndex !== scope.$index
                    "
                    @click="editLinkRowIndex = scope.$index"
                    >编辑</el-button
                  >
                  <el-button
                    link
                    type="danger"
                    size="small"
                    @click="handleDeleteLinkRow(scope.row.id)"
                    >删除</el-button
                  >
                </template>
              </el-table-column>
            </el-table>
            <el-button
              style="width: 100%"
              type="primary"
              :disabled="editLinkRowIndex !== -1"
              @click="handleAppendLink"
              >添加关系</el-button
            >
          </el-tab-pane>
        </el-tabs>
      </el-scrollbar>
    </div>
  </el-dialog>
</template>

<script setup lang="ts">
/**
 * 节点对象
 */
export interface RelativeNode extends d3.SimulationNodeDatum {
  /**
   * 节点唯一标识
   */
  id: string | number;
  /**
   * 节点名称
   */
  name: string;
  /**
   * 节点边框颜色
   */
  nodeStroke?: string;
  /**
   * 节点边框宽度
   */
  nodeStrokeWidth?: number;
  /**
   * 节点半径
   */
  nodeRadius?: number;
  /**
   * 节点文本字体大小
   */
  nodeLabelFontSize?: number;
  /**
   * 节点背景类型
   * @propertities 1 纯色
   * @propertities 2 图片
   */
  nodeBackgroundType?: "1" | "2";
  /**
   * 节点背景
   */
  nodeBackground?: string;
}

/**
 * 连线对象
 */
export interface RelativeLink {
  id: string;
  source: string;
  target: string;
  label: string;
  /**
   * 连接线颜色
   */
  linkStroke?: string;
  /**
   * 连接线宽度
   */
  linkStrokeWidth?: number;
  /**
   * 连接线文本字体大小
   */
  linkLabelFontSize?: number;
  /**
   * 连接线文本颜色
   */
  linkLabelColor?: string;
}

export interface DataPanelProps {
  /**
   * 节点列表 { @link RelativeNode }
   */
  nodes: RelativeNode[];
  /**
   * 连线列表 { @link RelativeLink }
   */
  links: RelativeLink[];
}

export type DataPanelNodeEventDetail = {
  index: number;
  row: RelativeNode;
};
export type DataPanelLinkEventDetail = {
  index: number;
  row: RelativeLink;
};

/**
 * 屏幕高度
 */
const maxHeight = window.innerHeight;

const props = defineProps<DataPanelProps>();

const emit = defineEmits<{
  (e: "onNodesChange", eventDetail: DataPanelNodeEventDetail): void;
  (e: "onLinksChange", eventDetail: DataPanelLinkEventDetail): void;
  (e: "deleteLink", id: string): void;
  (e: "deleteNode", id: string): void;
  (e: "appendNode", id: string): void;
}>();

type TabName = "node" | "link";
/**
 * 弹窗控制
 */
const visible = ref(false);
function toggleShowDialog(value: boolean) {
  visible.value = value === undefined ? !visible.value : value;
}
defineExpose({
  toggleShowDialog,
});

/**
 * 激活的tab
 */
const activeName = ref("node" as TabName);

/**
 * 节点列表
 */
const _nodes = ref(props.nodes.slice(0));
watch(props.nodes, () => {
  _nodes.value = props.nodes.slice(0);
});

/**
 * 当前编辑状态的节点序号
 */
const editNodeRowIndex = ref(-1);
/**
 * 编辑节点
 * @param row 新节点数据
 * @param index 序号
 */
function handleEditNodeRow(row: RelativeNode, index: number) {
  editNodeRowIndex.value = -1;
  emit("onNodesChange", { index, row });
}
/**
 * 删除节点
 * @param row 新节点数据
 */
function handleDeleteNodeRow(row: RelativeNode) {
  if (row.id === newNodeId.value) {
    newNodeId.value = "";
    editNodeRowIndex.value = -1;
    return void _nodes.value.pop();
  }
  ElMessageBox.confirm("确定删除该节点吗？", "warning").then(() => {
    emit("deleteNode", String(row.id));
    ElMessage({
      type: "success",
      message: "删除成功！",
    });
    if (newNodeId.value) {
      newNodeId.value = "";
      editNodeRowIndex.value = -1;
    }
    nextTick(() => {
      _nodes.value = props.nodes.slice(0);
      _links.value = props.links.slice(0);
    });
  });
}
/**
 * 新增节点ID
 */
const newNodeId = ref("");
/**
 * 点击新增节点
 */
function handleAppendNode() {
  if (newNodeId.value === _nodes.value[_nodes.value.length - 1].id) {
    return;
  }
  newNodeId.value = Date.now().toString(28);
  _nodes.value.push({
    id: newNodeId.value,
    name: "",
  });
  editNodeRowIndex.value = _nodes.value.length - 1;
}

/**
 * 关系列表
 */
const _links = ref(props.links.slice(0));
watch(props.links, () => {
  _links.value = props.links.slice(0);
});

/**
 * 当前编辑状态的关系序号
 */
const editLinkRowIndex = ref(-1);

/**
 * 编辑节点
 * @param row 新关系数据
 * @param index 序号
 */
function handleEditLinkRow(row: RelativeLink, index: number) {
  if (row.id === newLinkId.value) {
    // 新增关系
    if (row.source === row.target) {
      return void ElMessage.warning("起点和终点不能相同！");
    }
    if (
      props.links.find(
        (link) => link.source === row.source && link.target === row.target
      )
    ) {
      return void ElMessage.warning("已存在相同方向的关系！");
    }
    newLinkId.value = "";
  }
  editLinkRowIndex.value = -1;
  emit("onLinksChange", { index, row });
}

/**
 * 删除关系
 * @param row 关系数据
 */
function handleDeleteLinkRow(id: string) {
  ElMessageBox.confirm("确定删除该关系吗？", "warning").then(() => {
    emit("deleteLink", id);
    ElMessage({
      type: "success",
      message: "删除成功！",
    });
    nextTick(() => {
      _links.value = props.links.slice(0);
    });
  });
}

/**
 * 新关系ID
 */
const newLinkId = ref("");

/**
 * 点击新增关系
 */
function handleAppendLink() {
  console.log(newLinkId, _links.value);
  if (newLinkId.value === _links.value[_links.value.length - 1].id) {
    return;
  }
  newLinkId.value = Date.now().toString(28);
  _links.value.push({
    id: newLinkId.value,
    label: "",
    source: "",
    target: "",
  });
  editLinkRowIndex.value = _links.value.length - 1;
}

/**
 * 强制输入数字
 * @param field 字段
 * @param index 数据序号
 * @param val 输入值
 * @param dataField 数据对象
 */
function focusInputNumber(
  field: keyof RelativeNode | keyof RelativeLink,
  index: number,
  val: string,
  dataField: "_nodes" | "_links" = "_nodes"
) {
  if (val === "" || /\d+/.test(val)) {
    if (dataField === "_nodes") {
      _nodes.value.splice(index, 1, { ..._nodes.value[index], [field]: val });
    }
    if (dataField === "_links") {
      _links.value.splice(index, 1, { ..._links.value[index], [field]: val });
    }
  }
}
</script>

<script lang="ts">
import {
  defineComponent,
  ref,
  defineProps,
  defineEmits,
  defineExpose,
  watch,
  nextTick,
} from "vue";

import { ElMessage, ElMessageBox } from "element-plus";
export default defineComponent({
  name: "DataPanel",
});
</script>
<style lang="scss">
.table-node-background-preview {
  width: 32px;
  height: 32px;
  img {
    width: 100%;
    height: 100%;
  }
}
</style>
